package site.zhangzhuo.learn_springboot;

import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.*;
import org.springframework.core.io.support.SpringFactoriesLoader;
import site.zhangzhuo.learn_springboot.aop.MyService;
import site.zhangzhuo.learn_springboot.aop.MyServiceImpl;
import site.zhangzhuo.learn_springboot.beandefinition.Person;
import site.zhangzhuo.learn_springboot.beanimport.Book;
import site.zhangzhuo.learn_springboot.beanimport.BookRegistrar;
import site.zhangzhuo.learn_springboot.beanimport.BookShelf;
import site.zhangzhuo.learn_springboot.beanimport.Librarian;
import site.zhangzhuo.learn_springboot.componentscan.Exclude;
import site.zhangzhuo.learn_springboot.listener.CustomEventPublisher;
import site.zhangzhuo.learn_springboot.listener.UserRegistrationService;
import site.zhangzhuo.learn_springboot.postprocessor.Blue;
import site.zhangzhuo.learn_springboot.postprocessor.Yellow;
import site.zhangzhuo.learn_springboot.profileConditional.WeekendLibraryConfiguration;
import site.zhangzhuo.learn_springboot.proxy.HelloWorld;
import site.zhangzhuo.learn_springboot.proxy.PrintMethodInterceptor;
import site.zhangzhuo.learn_springboot.spi.MessageService;

import java.io.IOException;
import java.net.URL;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.List;
import java.util.ServiceLoader;

/*
这个注解会干扰Spring Boot的自动组件扫描，不知道为啥，还得再研究。。。
@ComponentScan(basePackages = "site.zhangzhuo.learn_springboot.componentscan",
		excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Exclude.class))
*/
@Import({Book.class, BookRegistrar.class})
@SpringBootApplication
@EnableAspectJAutoProxy
public class LearnSpringbootApplication {
	public static void main(String[] args) {
		ApplicationContext apcontext=SpringApplication.run(LearnSpringbootApplication.class, args);
		AnnotationConfigApplicationContext context=(AnnotationConfigApplicationContext)apcontext;

		//AOP
		MyService service = context.getBean(MyService.class);
		try {
			// 正常情况
			System.out.println("Calling performAction with 'test'");
			service.performAction("test");
			// 异常情况
			System.out.println("\nCalling performAction with 'error'");
			service.performAction("error");
		} catch (Exception e) {
			System.out.println("Exception caught in DemoApplication: " + e.getMessage());
		}

		/*
		// cglib动态代理
		Enhancer enhancer = new Enhancer();
		enhancer.setSuperclass(HelloWorld.class);
		enhancer.setCallback(new PrintMethodInterceptor());
		HelloWorld proxy = (HelloWorld) enhancer.create(); // 创建代理对象
		proxy.sayHello(); // 通过代理对象调用方法

		//JDK SPI 和 srpingboot spring.factories
		ServiceLoader<MessageService> loaders = ServiceLoader.load(MessageService.class);
		for (MessageService service : loaders) {
			System.out.println(service.getMessage());
		}
		System.out.println("---------------");
		List<MessageService> services = SpringFactoriesLoader.loadFactories(MessageService.class, null);
		for (MessageService service : services) {
			System.out.println(service.getMessage());
		}

		// BeanDefinition是AttributeAccessor的子接口
		BeanDefinition bd = new RootBeanDefinition(Person.class);
		bd.setAttribute("personAttr", "a value");
		if(bd.hasAttribute("personAttr")) {
			System.out.println("personAttr: " + bd.getAttribute("personAttr"));
			bd.removeAttribute("personAttr");
			System.out.println("personAttr: " + bd.getAttribute("personAttr"));
		}

		//@Componentscan filter
		String[] beanNames = context.getBeanDefinitionNames();
		for (String beanName : beanNames) {
			System.out.println(beanName);
		}

		//profile设置  JVM参数加上 -Dspring.profiles.active=weekend
		WeekendLibraryConfiguration bean = context.getBean(WeekendLibraryConfiguration.class);
		System.out.println(bean.weekendOpeningHours());
		//@conditional条件装配
		System.out.println("IOC容器是否有librarian3？" + context.containsBean("librarian3"));
		System.out.println("IOC容器是否有library3？" + context.containsBean("library3"));

		//Listener Event Publisher
		CustomEventPublisher publisher = context.getBean(CustomEventPublisher.class);
		publisher.doSomethingAndPublishAnEvent("hello world event");
		UserRegistrationService userRegistrationService = context.getBean(UserRegistrationService.class);
		userRegistrationService.registerUser("testUser");

		//-----------------BeanDefinition---------------------------------
		String personBeanName = "person";
		BeanDefinition personBeanDefinition = context.getBeanFactory().getBeanDefinition(personBeanName);
		// 获取Bean的类信息
		System.out.println("Bean Class Name: " + context.getBean(personBeanName).getClass().getName());
		// 获取Bean的属性
		System.out.println("Scope: " + personBeanDefinition.getScope());
		System.out.println("Is primary: " + personBeanDefinition.isPrimary());
		System.out.println("Description: " + personBeanDefinition.getDescription());
		// 获取Bean的行为特征
		System.out.println("Is lazy init: " + personBeanDefinition.isLazyInit());
		System.out.println("Init method: " + personBeanDefinition.getInitMethodName());
		System.out.println("Destroy method: " + personBeanDefinition.getDestroyMethodName());
		// 获取Bean的关系
		System.out.println("Parent bean name: " + personBeanDefinition.getParentName());
		System.out.println("Depends on: " + Arrays.toString(personBeanDefinition.getDependsOn()));
		// 获取Bean的配置属性
		System.out.println("Constructor argument values: " + personBeanDefinition.getConstructorArgumentValues());
		System.out.println("Property values: " + personBeanDefinition.getPropertyValues());
		//-----------------BeanDefinition---------------------------------

		// --------@Import导入多类----和自定义注解-----------
		String[] beanDefinitionNames = context.getBeanDefinitionNames();
		Librarian librarian = context.getBean(Librarian.class);
		BookShelf bookShelf = context.getBean("bookShelf", BookShelf.class);
		Book book1 = (Book) context.getBean("book1");
		Book book2 = (Book) context.getBean("book2");
		librarian.manage();
		bookShelf.getBooks().forEach(book -> System.out.println("Book: " + book.getName()));
		//ImportBeanDefinitionRegistrar导入类
		Book book3 = context.getBean("myBook", Book.class);
		System.out.println(book3.getName());
		// --------@Import导入多类----和自定义注解-----------

		//-----------------@Import装配模块---------------------------------
		//若果只用.class做参数会报错发现了3个Book.class Bean，加上name就唯一了,或着单例的直接用name
		Book book = context.getBean("site.zhangzhuo.learn_springboot.beanimport.Book",Book.class);
		//System.out.println(book);
		//-----------------@Import装配模块---------------------------------

		//--------------------PostProcessor---------------------------------
		Blue blue =apcontext.getBean(Blue.class);
		Yellow yellow =apcontext.getBean(Yellow.class);
		System.out.println(blue);
		System.out.println(yellow);
		//--------------------PostProcessor-------------------------------

		//--------------------Resources--------------------
		try {
			Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources("META-INF/spring.factories");
			while (resources.hasMoreElements()) {
				URL url = resources.nextElement();
				//System.out.println(url);
			}
		} catch (IOException e) {
			throw new RuntimeException(e);
		}
		//--------------------Resources--------------------
		*/
	}
}

